﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Quexcel.OpenXML.Interfaces;
using System.Xml;
using System.IO.Packaging;
using System.IO;
using System.Text.RegularExpressions;
using Quexcel.OpenXML.Utils;
using Quexcel.OpenXML.Excel.SharedString;

namespace Quexcel.OpenXML.Excel.Worksheet.IO
{
    internal class WorksheetWriter : WorksheetReader, IWorksheetWriter, IDisposable
    {
        internal WorksheetWriter(PackagePart sheetPart, SharedStringData sharedString, XmlNamespaceManager nsManager)
            : base(sheetPart, sharedString, nsManager) { }

        #region IWriter Members

        public bool Write(string addressName, string value)
        {
            string output = Utils.Util.CellAddressPattern.Match(addressName).Result("${cell}:${row}");
            int rowIndex = Convert.ToInt32(output.Split(':')[1]);

            XmlNode cellnode = worksheetDoc.SelectSingleNode(
                                      string.Format("//d:sheetData/d:row/d:c[@r='{0}']", addressName),
                                      nsManager);
            
            if (cellnode == null)
            {
                XmlElement cellElement = worksheetDoc.CreateElement("c", DocumentInfo.WorksheetSchema);
                cellElement.Attributes.Append(worksheetDoc.CreateAttribute("r"));
                cellElement.Attributes["r"].Value = addressName;

                XmlElement valueElement = worksheetDoc.CreateElement("v", DocumentInfo.WorksheetSchema);
                valueElement.InnerText = value.ToString();
                cellElement.AppendChild(valueElement);

                //  Default style is "0"
                cellElement.Attributes.Append(worksheetDoc.CreateAttribute("s"));
                cellElement.Attributes["s"].Value = "0";

                XmlNode rowNode = worksheetDoc.SelectSingleNode(string.Format(
                  "//d:sheetData/d:row[@r='{0}']", rowIndex), nsManager);
                if (rowNode == null)
                {
                    //  Didn't find the row, either. Just add a new row element:
                    XmlNode sheetDataNode = worksheetDoc.SelectSingleNode("//d:sheetData", nsManager);
                    if (sheetDataNode != null)
                    {
                        XmlElement rowElement = worksheetDoc.CreateElement("row", DocumentInfo.WorksheetSchema);
                        rowElement.Attributes.Append(worksheetDoc.CreateAttribute("r"));
                        rowElement.Attributes["r"].Value = rowIndex.ToString();
                        rowElement.AppendChild(cellElement);
                        sheetDataNode.AppendChild(rowElement);
                        return true;
                    }
                }
                else
                {
                    XmlAttribute styleAttr = ((XmlAttribute)(rowNode.Attributes.GetNamedItem("s")));
                    if (styleAttr != null)
                    {
                        cellElement.Attributes["s"].Value = styleAttr.Value;
                    }

                    XmlNode biggerNode = null;
                    XmlNodeList cellNodes = rowNode.SelectNodes("./d:c", nsManager);
                    if (cellNodes != null)
                    {
                        foreach (XmlNode node in cellNodes)
                        {
                            if (String.Compare(node.Attributes["r"].Value, addressName) > 0)
                            {
                                biggerNode = node;
                                break;
                            }
                        }
                    }
                    if (biggerNode == null)
                    {
                        rowNode.AppendChild(cellElement);
                    }
                    else
                    {
                        rowNode.InsertBefore(cellElement, biggerNode);
                    }
                    return true;
                }

            }
            else
            {
                cellnode.Attributes.RemoveNamedItem("t");
                XmlNode valueNode = cellnode.SelectSingleNode("d:v", nsManager);
                if (valueNode == null)
                {
                    //  Cell with deleted value. Add a value element now.
                    valueNode = worksheetDoc.CreateElement("v", DocumentInfo.WorksheetSchema);
                    cellnode.AppendChild(valueNode);
                }
                valueNode.InnerText = value.ToString();
                return true;
            }

            return true;
        }

        public bool Flush()
        {
            try
            {
                worksheetDoc.Save(sheetPart.GetStream(FileMode.Create, FileAccess.Write));
                return true;
            }
            catch
            {
                return false;
            }
            finally
            {
                this.Close();
            }
        }

        public void Close()
        {
            
        }

        #endregion

        public bool Reset()
        {
            try
            {
                worksheetDoc.Load(sheetPart.GetStream());
                return true;
            }
            catch
            {
                return false;
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            worksheetDoc = null;
            this.Close();
            base.Dispose();
        }

        #endregion

        #region IWorksheet Members

        public string WorksheetName
        {
            get { throw new NotImplementedException(); }
        }

        #endregion
    }
}
